package com.qiongqi.datadir.utils.file;

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import paillierp.Paillier;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.qiongqi.datadir.utils.security.HEUtils.paillierEncrypt;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.trim;

@Slf4j
public final class CsvEncUtils {
    /**
     * 使用 OpenCSV 5.12.0 对 CSV 输入流中的指定字段进行同态加密
     * 返回一个可读的加密后 CSV 流
     *
     * @param inputStream  原始 CSV 输入流
     * @param fields       需要加密的字段名列表
     * @param paillier    Paillier 公钥
     * @return 返回加密后的 CSV 输出流（调用方可读取）
     * @throws Exception 加密或流处理异常
     */
    public static File heEncFileAsStream(
            File inputFile,
            List<String> fields,
            Paillier paillier) throws Exception {

        // 创建临时文件（确保唯一性）
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
                .withZone(ZoneId.systemDefault());
        String timestampStr = formatter.format(Instant.now());
        File tempFile  = File.createTempFile("encrypted-csv-" + timestampStr, ".tmp");
        tempFile.deleteOnExit();

        Thread t = new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new FileReader(inputFile, StandardCharsets.UTF_8));
                 CSVReader csvReader = new CSVReader(reader);
                 // 将输出目标改为临时文件
                 FileWriter fileWriter = new FileWriter(tempFile, StandardCharsets.UTF_8);
                 CSVWriter csvWriter = new CSVWriter(fileWriter)) {

                // 读取并写入Header
                String[] header = csvReader.readNext();
                if (header == null || header.length == 0) {
                    log.error("CSV文件为空或解析失败");
                    return;
                }
                csvWriter.writeNext(header);
                log.info("Header: {}", ArrayUtils.toString(header));

                // 构建需要加密的列索引集合
                List<String> headerList = Arrays.asList(header);
                Set<Integer> encryptIndices = new HashSet<>();
                for (String field : fields) {
                    int idx = headerList.indexOf(field);
                    if (idx != -1) {
                        encryptIndices.add(idx);
                    }
                }

                // 逐行处理并写入临时文件
                String[] line;
                while ((line = csvReader.readNext()) != null) {
                    String[] encryptedLine = new String[line.length];
                    for (int i = 0; i < line.length; i++) {
                        if (encryptIndices.contains(i)) {
                            String value = line[i];
                            if (isNumeric(value)) {
                                try {
                                    // 执行同态加密
                                    encryptedLine[i] = paillierEncrypt(paillier, trim(value));
                                } catch (Exception e) {
                                    log.error("加密失败{}: ，使用原文",  value);
                                    encryptedLine[i] = value; // 可选：打码或抛错
                                }
                            } else {
                                encryptedLine[i] = value; // 非数字不加密
                            }
                        } else {
                            encryptedLine[i] = line[i];
                        }
                    }
                    csvWriter.writeNext(encryptedLine);
                }
                csvWriter.flush(); // 确保数据刷入磁盘
            } catch (Exception e) {
                log.error("CSV流处理失败", e);
                throw new RuntimeException(e);
            } finally {
                // 不再需要手动关闭PipedOutputStream
            }
        });

        t.setDaemon(true);
        t.start();
        t.join(); // 等待线程完成写入（确保文件已生成）[9](@ref)

        return tempFile; // 返回临时文件的输入流
    }

    /**
     * 判断字符串是否为有效数字
     */
    private static boolean isNumeric(String str) {
        if (isBlank(str)) return false;
        try {
            Double.parseDouble(str.trim());
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}
